﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TruckWatch.Cloud.Models;
using TruckWatch.Cloud.DAL;
using TruckWatch.Cloud.Models.ViewModels;
using TruckWatch.Cloud.BusinessLogic.Util;
using TruckWatch.Cloud.Models.EF;

namespace TruckWatch.Cloud.BusinessLogic
{
    public static class DriverBL
    {
        public static DriverModel GetDriverModelbyId(int driverId, bool includeLocationAddress)
        {
            DriverModel driverModel = new DriverModel();

            TruckWatch.Cloud.Models.EF.TruckWatchEntities context = new TruckWatch.Cloud.Models.EF.TruckWatchEntities();

            // Driver Details
            driverModel.Driver = DriverDAL.GetDriverById(driverId);
            if (driverModel.Driver == null)
                return null;

            // All Trips

            List<TruckWatch.Cloud.Models.EF.Trip> trips =
                driverModel.AllTrips = (from t in context.Trips where t.DriverId == driverModel.Driver.Id select t).ToList();

            // All TripPoints
            driverModel.TripPoints = context.TripPoints.Where(x => x.DriverId == driverModel.Driver.Id)
                .OrderByDescending(x => x.ArriveDate).ToList();

            // TripStatuses
            driverModel.TripStatuses = context.TripStatus1.Where(x => x.DriverId == driverModel.Driver.Id)
                .OrderByDescending(x => x.StatusDate).ToList();

            // Received Messages
            driverModel.MessagesReceived = MessageBL.GetAllMessagesForUserId(driverModel.Driver.UserId);

            // Last TripPoint
            TripPointNEF lastTripPoint = LastTripPointForDriver(driverId);
            if (lastTripPoint != null)
            {
                driverModel.LastLocationLatLngString = lastTripPoint.Latitude.ToString("0.00") + " " + lastTripPoint.Longitude.ToString("0.00");
                driverModel.LastLocationReportTimeDisplay = lastTripPoint.ArriveDate.ToString("yyyy-MM-dd HH:mm") + " GMT";
                if (includeLocationAddress)
                    driverModel.LastLocationAddress = GoogleMaps.GetAddress(lastTripPoint);
            }
            else
            {
                driverModel.LastLocationLatLngString = "(location not reported)";
            }

            // Last StatusMessage
            var statuses = TripStatusDAL.GetAllTripStatusesByDriver(driverModel.Driver.Id);
            if (statuses != null && statuses.Count > 0)
            {
                TripStatusNEF lastStatus = statuses.Last();
                driverModel.LastMessage = lastStatus.StatusMessage;
                driverModel.LastMessageReportTime = lastStatus.StatusDate.ToString("yyyy-MM-dd HH:mm") + " GMT";
            }
            else
            {
                driverModel.LastMessage = "(status not reported)";
            }

            return driverModel;
        }

        private static TripPointNEF LastTripPointForDriver(int driverId)
        {
            return TripPointDAL.GetAllTripPointsByDriverId(driverId).LastOrDefault();
        }

        public static List<DriverLocation> GetAllDriverLocations(GeoPoint currentDriverLocation, bool IncludeAddress)
        {
            List<DriverLocation> dLocations = new List<DriverLocation>();

            var ret = DriverDAL.GetAllDriverSummaries();

            foreach (DriverSummary summary in ret)
            {
                DriverModel fullDriver = DriverBL.GetDriverModelbyId(summary.Id, IncludeAddress);

                DriverLocation dLocation = new DriverLocation();
                dLocation.Id = fullDriver.Driver.Id;
                dLocation.UserId = fullDriver.Driver.UserId;
                dLocation.Name = fullDriver.Driver.Name;
                dLocation.TruckType = fullDriver.Driver.TruckType;

                //
                TripPointNEF lastTripPoint = LastTripPointForDriver(fullDriver.Driver.Id);
                if (lastTripPoint != null)
                {
                    dLocation.Latitude = lastTripPoint.Latitude;
                    dLocation.Longitude = lastTripPoint.Longitude;
                    dLocation.LocationReportDate = lastTripPoint.ArriveDate;
                    dLocation.LocationAddress = fullDriver.LastLocationAddress;
                }

                //
                TripStatusNEF lastStatus = TripStatusDAL.GetAllTripStatusesByDriver(fullDriver.Driver.Id).LastOrDefault();
                dLocation.LastStatusMessage = lastStatus == null ? null : lastStatus.StatusMessage;

                // Distance ToMe
                if (currentDriverLocation != null)
                    dLocation.DistanceToMe = LatLongUtil.DistanceInMiles(currentDriverLocation, new GeoPoint(dLocation.Latitude, dLocation.Longitude));

                // add to list
                dLocations.Add(dLocation);
            }

            return dLocations;
        }

        public static List<DriverLocation> GetAllDriverLocationsNearMe(GeoPoint currentDriverLocation, double radius, bool IncludeAddress)
        {
            var allDriverLocations = GetAllDriverLocations(currentDriverLocation, IncludeAddress);
            var filtered = allDriverLocations.Where(x => x.DistanceToMe != 0 && x.DistanceToMe <= radius).ToList();

            return filtered;
        }

        public static DriverLocation GetNthClosestDriverLocationToMe(GeoPoint currentDriverLocation, int nth)
        {
            var allDriverLocations = GetAllDriverLocations(currentDriverLocation, false);
            var filtered = allDriverLocations.Where(x => x.DistanceToMe != 0).OrderBy(x => x.DistanceToMe).ToList();

            if (filtered.Count - 1 < nth)
                throw new Exception(string.Format("Unable to take {0} nth closest driver, there are only {1} drivers close to you.", nth, filtered.Count));

            return filtered.Skip(nth - 1).FirstOrDefault();
        }

        public static double GetDistanceBetweenDrivers(int driver1id, int driver2id)
        {
            TripPointNEF driver1Point = LastTripPointForDriver(driver1id);
            TripPointNEF driver2Point = LastTripPointForDriver(driver2id);

            return LatLongUtil.DistanceInMiles(driver1Point, driver2Point);
        }


    }
}
